import { Op, fn, col, literal } from 'sequelize';
import { Article, User, Comment, Tag, Category } from '../models';
import { getDateRange } from '../utils/date';

/**
 * 统计数据周期类型
 */
type StatsPeriod = 'today' | 'week' | 'month' | 'year' | 'all';

/**
 * 统计数据类型
 */
interface StatsData {
  count: number;
  [key: string]: any;
}

/**
 * 分类统计数据
 */
interface CategoryStats extends StatsData {
  id: string;
  name: string;
  article_count: number;
}

/**
 * 标签统计数据
 */
interface TagStats extends StatsData {
  id: string;
  name: string;
  article_count: number;
}

/**
 * 统计服务
 * @description 提供各种统计数据和分析功能
 */
class StatisticsService {
  /**
   * 获取文章统计数据
   * @param period 统计周期
   * @returns 文章统计数据
   */
  public async getArticleStats(period: StatsPeriod = 'all'): Promise<StatsData> {
    try {
      const whereClause = this.getDateRangeCondition('created_at', period);

      const totalCount = await Article.count({
        where: whereClause,
      });

      const publishedCount = await Article.count({
        where: {
          ...whereClause,
          status: 'published',
        },
      });

      const draftCount = await Article.count({
        where: {
          ...whereClause,
          status: 'draft',
        },
      });

      // 获取平均阅读量
      const viewStats = await Article.findOne({
        attributes: [
          [fn('AVG', col('view_count')), 'avg_views'],
          [fn('SUM', col('view_count')), 'total_views'],
        ],
        where: {
          ...whereClause,
          status: 'published',
        },
        raw: true,
      });

      // 获取平均评论量
      const commentStats = await Article.findOne({
        attributes: [
          [fn('AVG', col('comment_count')), 'avg_comments'],
          [fn('SUM', col('comment_count')), 'total_comments'],
        ],
        where: {
          ...whereClause,
          status: 'published',
        },
        raw: true,
      });

      return {
        count: totalCount,
        published_count: publishedCount,
        draft_count: draftCount,
        avg_views: Math.round(Number(viewStats?.avg_views || 0)),
        total_views: Number(viewStats?.total_views || 0),
        avg_comments: Math.round(Number(commentStats?.avg_comments || 0)),
        total_comments: Number(commentStats?.total_comments || 0),
      };
    } catch (error) {
      console.error('获取文章统计数据失败:', error);
      throw error;
    }
  }

  /**
   * 获取用户统计数据
   * @param period 统计周期
   * @returns 用户统计数据
   */
  public async getUserStats(period: StatsPeriod = 'all'): Promise<StatsData> {
    try {
      const whereClause = this.getDateRangeCondition('created_at', period);

      const totalCount = await User.count({
        where: whereClause,
      });

      const activeCount = await User.count({
        where: {
          ...whereClause,
          status: 'active',
        },
      });

      const inactiveCount = await User.count({
        where: {
          ...whereClause,
          status: 'inactive',
        },
      });

      return {
        count: totalCount,
        active_count: activeCount,
        inactive_count: inactiveCount,
      };
    } catch (error) {
      console.error('获取用户统计数据失败:', error);
      throw error;
    }
  }

  /**
   * 获取评论统计数据
   * @param period 统计周期
   * @returns 评论统计数据
   */
  public async getCommentStats(period: StatsPeriod = 'all'): Promise<StatsData> {
    try {
      const whereClause = this.getDateRangeCondition('created_at', period);

      const totalCount = await Comment.count({
        where: whereClause,
      });

      const approvedCount = await Comment.count({
        where: {
          ...whereClause,
          status: 'approved',
        },
      });

      const pendingCount = await Comment.count({
        where: {
          ...whereClause,
          status: 'pending',
        },
      });

      const rejectedCount = await Comment.count({
        where: {
          ...whereClause,
          status: 'rejected',
        },
      });

      return {
        count: totalCount,
        approved_count: approvedCount,
        pending_count: pendingCount,
        rejected_count: rejectedCount,
      };
    } catch (error) {
      console.error('获取评论统计数据失败:', error);
      throw error;
    }
  }

  /**
   * 获取分类统计数据
   * @returns 分类统计数据
   */
  public async getCategoryStats(): Promise<CategoryStats[]> {
    try {
      // 获取每个分类下的文章数量
      const categories = await Category.findAll({
        attributes: [
          'id',
          'name',
          [
            literal('(SELECT COUNT(*) FROM articles WHERE articles.category_id = Category.id AND articles.deleted_at IS NULL)'),
            'article_count',
          ],
        ],
        order: [[literal('article_count'), 'DESC']],
        raw: true,
      }) as unknown as CategoryStats[];

      // 计算总数
      let totalArticles = 0;
      categories.forEach((category) => {
        totalArticles += category.article_count;
      });

      // 添加百分比
      return categories.map((category) => ({
        ...category,
        count: category.article_count,
        percentage: totalArticles > 0 ? Math.round((category.article_count / totalArticles) * 100) : 0,
      }));
    } catch (error) {
      console.error('获取分类统计数据失败:', error);
      throw error;
    }
  }

  /**
   * 获取标签统计数据
   * @param limit 限制返回数量
   * @returns 标签统计数据
   */
  public async getTagStats(limit: number = 20): Promise<TagStats[]> {
    try {
      // 获取每个标签下的文章数量
      const tags = await Tag.findAll({
        attributes: [
          'id',
          'name',
          [
            literal('(SELECT COUNT(*) FROM article_tags WHERE article_tags.tag_id = Tag.id)'),
            'article_count',
          ],
        ],
        order: [[literal('article_count'), 'DESC']],
        limit,
        raw: true,
      }) as unknown as TagStats[];

      // 计算总数
      let totalArticles = 0;
      tags.forEach((tag) => {
        totalArticles += tag.article_count;
      });

      // 添加百分比
      return tags.map((tag) => ({
        ...tag,
        count: tag.article_count,
        percentage: totalArticles > 0 ? Math.round((tag.article_count / totalArticles) * 100) : 0,
      }));
    } catch (error) {
      console.error('获取标签统计数据失败:', error);
      throw error;
    }
  }

  /**
   * 获取热门文章
   * @param limit 限制返回数量
   * @param days 统计天数，默认30天
   * @returns 热门文章列表
   */
  public async getPopularArticles(limit: number = 10, days: number = 30): Promise<Article[]> {
    try {
      const date = new Date();
      date.setDate(date.getDate() - days);

      const articles = await Article.findAll({
        where: {
          status: 'published',
          created_at: {
            [Op.gte]: date,
          },
        },
        attributes: [
          'id',
          'title',
          'slug',
          'summary',
          'view_count',
          'comment_count',
          'created_at',
          'updated_at',
        ],
        order: [
          ['view_count', 'DESC'],
          ['comment_count', 'DESC'],
        ],
        limit,
        include: [
          {
            model: Category,
            as: 'category',
            attributes: ['id', 'name', 'slug'],
          },
          {
            model: Tag,
            as: 'tags',
            attributes: ['id', 'name', 'slug'],
            through: { attributes: [] },
          },
        ],
      });

      return articles;
    } catch (error) {
      console.error('获取热门文章失败:', error);
      throw error;
    }
  }

  /**
   * 获取文章趋势数据
   * @param period 统计周期
   * @returns 文章趋势数据
   */
  public async getArticleTrend(period: StatsPeriod = 'month'): Promise<any[]> {
    try {
      let dateFormat: string;
      let groupByField: string;

      // 根据周期选择合适的日期格式和分组字段
      switch (period) {
        case 'today':
          dateFormat = '%Y-%m-%d %H:00:00';
          groupByField = 'hour';
          break;
        case 'week':
          dateFormat = '%Y-%m-%d';
          groupByField = 'day';
          break;
        case 'month':
          dateFormat = '%Y-%m-%d';
          groupByField = 'day';
          break;
        case 'year':
          dateFormat = '%Y-%m';
          groupByField = 'month';
          break;
        default:
          dateFormat = '%Y-%m';
          groupByField = 'month';
      }

      const whereClause = this.getDateRangeCondition('created_at', period);

      // 获取时间段内每天/每月的文章发布数量
      const trend = await Article.findAll({
        attributes: [
          [fn('DATE_FORMAT', col('created_at'), dateFormat), groupByField],
          [fn('COUNT', col('id')), 'count'],
        ],
        where: whereClause,
        group: [groupByField],
        order: [[col(groupByField), 'ASC']],
        raw: true,
      });

      return trend;
    } catch (error) {
      console.error('获取文章趋势数据失败:', error);
      throw error;
    }
  }

  /**
   * 获取用户增长趋势数据
   * @param period 统计周期
   * @returns 用户增长趋势数据
   */
  public async getUserGrowthTrend(period: StatsPeriod = 'month'): Promise<any[]> {
    try {
      let dateFormat: string;
      let groupByField: string;

      // 根据周期选择合适的日期格式和分组字段
      switch (period) {
        case 'today':
          dateFormat = '%Y-%m-%d %H:00:00';
          groupByField = 'hour';
          break;
        case 'week':
          dateFormat = '%Y-%m-%d';
          groupByField = 'day';
          break;
        case 'month':
          dateFormat = '%Y-%m-%d';
          groupByField = 'day';
          break;
        case 'year':
          dateFormat = '%Y-%m';
          groupByField = 'month';
          break;
        default:
          dateFormat = '%Y-%m';
          groupByField = 'month';
      }

      const whereClause = this.getDateRangeCondition('created_at', period);

      // 获取时间段内每天/每月的用户注册数量
      const trend = await User.findAll({
        attributes: [
          [fn('DATE_FORMAT', col('created_at'), dateFormat), groupByField],
          [fn('COUNT', col('id')), 'count'],
        ],
        where: whereClause,
        group: [groupByField],
        order: [[col(groupByField), 'ASC']],
        raw: true,
      });

      return trend;
    } catch (error) {
      console.error('获取用户增长趋势数据失败:', error);
      throw error;
    }
  }

  /**
   * 根据统计周期获取日期范围条件
   * @param field 日期字段名
   * @param period 统计周期
   * @returns 日期范围条件
   */
  private getDateRangeCondition(field: string, period: StatsPeriod): any {
    // 默认条件，不包括已删除的记录
    const condition: any = {};

    if (period === 'all') {
      return condition;
    }

    // 获取指定周期的日期范围
    const [startDate, endDate] = getDateRange(period);

    // 添加日期范围条件
    condition[field] = {
      [Op.between]: [startDate, endDate],
    };

    return condition;
  }
}

export default new StatisticsService();
